﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Windows.Forms;
using System.Xml;
using System.Runtime.InteropServices;

namespace ProfManager
{
    public class ProfileManager
    {
        [DllImport("kernel32")]
        private static extern long WritePrivateProfileString(string section, string key, string val, string filePath);
        [DllImport("kernel32")]
        private static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int size, string filePath);

        string baseFolder;
        bool reqUser;
        string[] files;

        string currentProfile;

        /// <summary>
        /// Check to make sure the base directory exists.  Create it if it doesn't.
        /// </summary>
        /// <returns></returns>
        private bool checkBaseFolder()
        {
            if (!Directory.Exists(baseFolder.TrimEnd('\\')))
            {
                Directory.CreateDirectory(baseFolder.TrimEnd('\\'));
                return false;
            }

            return true;
        }

        /// <summary>
        /// Returns true iff:
        ///     The file that lists all profiles exists.
        ///     All files for the current profile exist.
        /// </summary>
        /// <returns></returns>
        private bool checkForExistingFiles()
        {
            if (!File.Exists(baseFolder + "CurProfile.txt"))
            {
                File.Create(baseFolder + "CurProfile.txt").Close();
                return false;
            }

            try
            {
                IEnumerable<string> s = File.ReadAllLines(baseFolder + "CurProfile.txt");
                currentProfile = s.ElementAt(0);
            }
            catch (Exception)
            {
                return false;
            }

            if (!Directory.Exists(baseFolder + currentProfile))
                return false;

            foreach (string s in files)
                if (!File.Exists(baseFolder + currentProfile + "\\" + s))
                    return false;

            return true;
        }

        /// <summary>
        /// Create all the files for the required profile.  If the file already exists, do nothing.
        /// </summary>
        /// <param name="profName"></param>
        /// <returns></returns>
        private bool createFilesForProfile(string profName)
        {
            if (profName == "")
                return false;

            if (!Directory.Exists(baseFolder + currentProfile))
                Directory.CreateDirectory(baseFolder + currentProfile);

            foreach (string s in files)
                if (!File.Exists(baseFolder + currentProfile + "\\" + s))
                    File.Create(baseFolder + currentProfile + "\\" + s).Close();

            return true;
        }

        /// <summary>
        /// Wrapper for creating files for the current profile.
        /// </summary>
        /// <returns></returns>
        private bool createFilesForProfile()
        {
            return createFilesForProfile(currentProfile);
        }

        /// <summary>
        /// Make sure the given fileName is a known file to manage.
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        private bool checkFileName(string fileName)
        {
            foreach (string s in files)
                if (s == fileName)
                    return true;

            return false;
        }

        /// <summary>
        /// Check to see if the number is a valid index into the file array.
        /// Returns true if number is a valid index, false otherwise.
        /// </summary>
        /// <param name="fileNum"></param>
        /// <returns></returns>
        private bool checkFileNum(int fileNum)
        {
            if (fileNum >= files.Length)
                return false;

            return true;
        }

        /// <summary>
        /// Constructor for ProfileManager class.  loc is the folder that will store profile files.
        /// fullPath specifies if this is an absolute path or not.
        /// requireUser will ensure that an active profile is selected at all times.  Otherwise, it will prompt the user for a profile choice.
        /// </summary>
        /// <param name="loc"></param>
        /// <param name="fullPath"></param>
        /// <param name="requireUser"></param>
        /// <param name="fileNames"></param>
        public ProfileManager(string loc, bool fullPath, bool requireUser, params string[] fileNames)
        {
            if (!fullPath)
                baseFolder = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\" + loc;
            else
                baseFolder = loc;
            if (!baseFolder.EndsWith("\\"))
                baseFolder += "\\";
            reqUser = requireUser;

            files = fileNames;

            checkBaseFolder();

            if (!checkForExistingFiles() && requireUser)
                PromptForUser();
        }

        /// <summary>
        /// Ask the user to choose a profile.  If requireUser is true, this will keep asking until the user enters a valid profile.
        /// </summary>
        public void PromptForUser()
        {
            UserSelect u = new UserSelect(Directory.EnumerateDirectories(baseFolder));
            while (u.ShowDialog() != System.Windows.Forms.DialogResult.OK)
            { }

            currentProfile = u.selectedProf;

            try
            {
                StreamWriter SW = File.CreateText(baseFolder + "CurProfile.txt");
                SW.WriteLine(currentProfile);
                SW.Close();
            }
            catch (Exception ex) { MessageBox.Show(ex.ToString()); }

            if (!Directory.Exists(baseFolder + currentProfile))
            {
                Directory.CreateDirectory(baseFolder + currentProfile);
            }

            foreach (string s in files)
                if (!File.Exists(baseFolder + currentProfile + "\\" + s))
                    File.Create(baseFolder + currentProfile + "\\" + s).Close();
        }

        /// <summary>
        /// Gets the base folder that will be used to store all these files.
        /// </summary>
        public string BaseFolder
        {
            get
            {
                return baseFolder;
            }
            set
            {
                baseFolder = value;
            }
        }

        /// <summary>
        /// Gets/Sets for the name of the current profile.
        /// </summary>
        public string CurrentProfile
        {
            get
            {
                return currentProfile;
            }
            set
            {
                currentProfile = value;
            }
        }

        /// <summary>
        /// Gets the list of all profiles.
        /// </summary>
        /// <returns></returns>
        public IEnumerable<string> AllProfiles()
        {
            return Directory.EnumerateDirectories(baseFolder);
        }

        /// <summary>
        /// Recreates all the files for the user.
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        public bool RecreateUserProfiles(string user)
        {
            if (Directory.Exists(baseFolder + currentProfile))
                try
                {
                    Directory.Delete(baseFolder + currentProfile, true);
                }
                catch (Exception)
                {
                    return false;
                }

            try
            {
                createFilesForProfile();
            }
            catch (Exception) { return false; }

            return true;
        }

        /// <summary>
        /// Recreates all the files for the currently selected user.
        /// </summary>
        /// <returns></returns>
        public bool RecreateUserProfiles()
        {
            return RecreateUserProfiles(CurrentProfile);
        }

        /// <summary>
        /// Gets the specified file for the specified user.
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="user"></param>
        /// <returns></returns>
        public StreamReader GetProfileFile(string fileName, string user)
        {
            if (!checkFileName(fileName))
                return null;

            return File.OpenText(baseFolder + user + "\\" + fileName);
        }

        /// <summary>
        /// Gets the specified file for the specified user.
        /// </summary>
        /// <param name="fileNum"></param>
        /// <param name="user"></param>
        /// <returns></returns>
        public StreamReader GetProfileFile(int fileNum, string user)
        {
            if (!checkFileNum(fileNum))
                return null;

            return GetProfileFile(files[fileNum]);
        }

        /// <summary>
        /// Gets the specified file for the currently selected user.
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public StreamReader GetProfileFile(string fileName)
        {
            return GetProfileFile(fileName, currentProfile);
        }

        /// <summary>
        /// Gets the specified file for the currently selected user.
        /// </summary>
        /// <param name="fileNum"></param>
        /// <returns></returns>
        public StreamReader GetProfileFile(int fileNum)
        {
            return GetProfileFile(fileNum, currentProfile);
        }

        /// <summary>
        /// Append the desired text to the end of the specified file for the specified user.
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="toAppend"></param>
        /// <param name="user"></param>
        /// <returns></returns>
        public bool AppendTextToProfileFile(string fileName, string toAppend, string user)
        {
            if (!checkFileName(fileName))
                return false;

            try
            {
                StreamWriter SW = File.AppendText(baseFolder + currentProfile + "\\" + fileName);
                SW.WriteLine(toAppend);
                SW.Close();
            }
            catch (Exception)
            {
                return false;
            }

            return true;
        }

        /// <summary>
        /// Append the desired text to the end of the specified file for the specified user.
        /// </summary>
        /// <param name="fileNum"></param>
        /// <param name="toAppend"></param>
        /// <param name="user"></param>
        /// <returns></returns>
        public bool AppendTextToProfileFile(int fileNum, string toAppend, string user)
        {
            if (!checkFileNum(fileNum))
                return false;

            return AppendTextToProfileFile(files[fileNum], toAppend, user);
        }

        /// <summary>
        /// Append the desired text to the end of the specified file for the currently selected user.
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="toAppend"></param>
        /// <returns></returns>
        public bool AppendTextToProfileFile(string fileName, string toAppend)
        {
            return AppendTextToProfileFile(fileName, toAppend, currentProfile);
        }

        /// <summary>
        /// Append the desired text to the end of the specified file for the currently selected user.
        /// </summary>
        /// <param name="fileNum"></param>
        /// <param name="toAppend"></param>
        /// <returns></returns>
        public bool AppendTextToProfileFile(int fileNum, string toAppend)
        {
            return AppendTextToProfileFile(fileNum, toAppend, currentProfile);
        }

        /// <summary>
        /// Write the text to the specified file for the specified user.  This will override any existing text in the file.
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="toAppend"></param>
        /// <param name="user"></param>
        /// <returns></returns>
        public bool WriteTextToProfileFile(string fileName, string toAppend, string user)
        {
            if (!checkFileName(fileName))
                return false;

            try
            {
                StreamWriter SW = File.CreateText(baseFolder + user + "\\" + fileName);
                SW.WriteLine(toAppend);
                SW.Close();
            }
            catch (Exception)
            {
                return false;
            }

            return true;
        }

        /// <summary>
        /// Write the text to the specified file for the specified user.  This will override any existing text in the file.
        /// </summary>
        /// <param name="fileNum"></param>
        /// <param name="toAppend"></param>
        /// <param name="user"></param>
        /// <returns></returns>
        public bool WriteTextToProfileFile(int fileNum, string toAppend, string user)
        {
            if (!checkFileNum(fileNum))
                return false;

            return WriteTextToProfileFile(files[fileNum], toAppend, user);
        }

        /// <summary>
        /// Write the text to the specified file for the currently selected user.  This will override any existing text in the file.
        /// </summary>
        /// <param name="fileName">Name of the file to write text to.</param>
        /// <param name="toAppend">String to write to the file.</param>
        /// <returns></returns>
        public bool WriteTextToProfileFile(string fileName, string toAppend)
        {
            return WriteTextToProfileFile(fileName, toAppend, currentProfile);
        }

        /// <summary>
        /// Write the text to the specified file for the currently selected user.  This will override any exisiting text in the file.
        /// </summary>
        /// <param name="fileNum">An index into the file list.</param>
        /// <param name="toAppend">String to write to file.</param>
        /// <returns></returns>
        public bool WriteTextToProfileFile(int fileNum, string toAppend)
        {
            return WriteTextToProfileFile(fileNum, toAppend, currentProfile);
        }

        /// <summary>
        /// Will check the specified file for the specified user and determine if its empty or doesn't exist.
        /// </summary>
        /// <param name="fileName">Name of the file to check for emptiness.</param>
        /// <param name="user">Name of the user to check for.</param>
        /// <returns></returns>
        public bool IsFileEmpty(string fileName, string user)
        {
            if (!checkFileName(fileName))
                return true;

            if (!Directory.Exists(baseFolder + user))
                return true;

            if (!File.Exists(baseFolder + user + "\\" + fileName))
                return true;

            if (File.ReadAllBytes(baseFolder + user + "\\" + fileName).Length == 0)
                return true;

            return false;
        }

        /// <summary>
        /// Will check the specified file for the specified user and determine if its empty or doesn't exist.
        /// </summary>
        /// <param name="fileNum">An index into the file list.</param>
        /// <param name="user">Name of the user to check for.</param>
        /// <returns></returns>
        public bool IsFileEmpty(int fileNum, string user)
        {
            if (!checkFileNum(fileNum))
                return true;

            return IsFileEmpty(files[fileNum], user);
        }

        /// <summary>
        /// Will check the specified file for the currently selected user and determine if its empty or doesn't exist.
        /// </summary>
        /// <param name="fileNum">An index into the file list.</param>
        /// <returns></returns>
        public bool IsFileEmpty(int fileNum)
        {
            return IsFileEmpty(fileNum, currentProfile);
        }

        /// <summary>
        /// Will check the specified file for the currently selected user and determine if its empty or doesn't exist.
        /// </summary>
        /// <param name="file">Name of file to check to see if its empty.</param>
        /// <returns></returns>
        public bool IsFileEmpty(string file)
        {
            return IsFileEmpty(file, currentProfile);
        }

        /// <summary>
        /// Writes a key/value pair into the desired section of the specified file.
        /// </summary>
        /// <param name="fileName">Name of file to get value for.</param>
        /// <param name="section">Section of the INI file this value is in.</param>
        /// <param name="key">Key to write.</param>
        /// <param name="value">Value to write to file.</param>
        /// <param name="user">Name of user that owns this file.</param>
        /// <returns></returns>
        public bool writeINIValue(string fileName, string section, string key, string value, string user)
        {
            return WritePrivateProfileString(section, key, value, baseFolder + currentProfile + "\\" + fileName) != 0;
        }

        /// <summary>
        /// Writes a key/value pair into the desired section of the specified file.
        /// </summary>
        /// <param name="fileNum">An index into the list of files.</param>
        /// <param name="section">Section of the INI file this value is in.</param>
        /// <param name="key">Key to write.</param>
        /// <param name="value">Value to write to file.</param>
        /// <param name="user">Name of user that owns this file.</param>
        /// <returns></returns>
        public bool writeINIValue(int fileNum, string section, string key, string value, string user)
        {
            return writeINIValue(files[fileNum], section, key, value, user);
        }

        /// <summary>
        /// Writes a key/value pair into the desired section of the specified file.
        /// </summary>
        /// <param name="fileName">Name of file to get value from.</param>
        /// <param name="section">Section of the INI file this value is in.</param>
        /// <param name="key">Key to write.</param>
        /// <param name="value">Value to write to file.</param>
        /// <returns></returns>
        public bool writeINIValue(string fileName, string section, string key, string value)
        {
            return writeINIValue(fileName, section, key, value, CurrentProfile);
        }

        /// <summary>
        /// Writes a key/value pair into the desired section of the specified file.
        /// </summary>
        /// <param name="fileNum">An index into the list of files.</param>
        /// <param name="section">Section of the INI file this value is in.</param>
        /// <param name="key">Key to write.</param>
        /// <param name="value">Value to write to file.</param>
        /// <returns></returns>
        public bool writeINIValue(int fileNum, string section, string key, string value)
        {
            return writeINIValue(fileNum, section, key, value, CurrentProfile);
        }

        /// <summary>
        /// Attempt to read a value from an INI file.
        /// </summary>
        /// <param name="fileName">Name of the file to get value from.</param>
        /// <param name="section">Section of the INI file this value is in.</param>
        /// <param name="key">Key to lookup.</param>
        /// <param name="user">Name of user to lookup this value for.</param>
        /// <returns></returns>
        public string readINIValue(string fileName, string section, string key, string user)
        {
            StringBuilder b = new StringBuilder(128);
            int i = GetPrivateProfileString(section, key, "", b, 128, baseFolder + currentProfile + "\\" + fileName);
            return b.ToString();
        }

        /// <summary>
        /// Attempt to read a value from an INI file.
        /// </summary>
        /// <param name="fileNum">An index into the list of files.</param>
        /// <param name="section">Section of the INI file this value is in.</param>
        /// <param name="key">Key to lookup.</param>
        /// <param name="user">Name of user to lookup this value for.</param>
        /// <returns></returns>
        public string readINIValue(int fileNum, string section, string key, string user)
        {
            return readINIValue(files[fileNum], section, key, user);
        }

        /// <summary>
        /// Attempt to read a value from an INI file.
        /// </summary>
        /// <param name="fileName">Name of file to get value from.</param>
        /// <param name="section">Section of the INI file this value is in.</param>
        /// <param name="key">Key to lookup.</param>
        /// <returns></returns>
        public string readINIValue(string fileName, string section, string key)
        {
            return readINIValue(fileName, section, key, CurrentProfile);
        }

        /// <summary>
        /// Attempt to read a value from an INI file.
        /// </summary>
        /// <param name="fileNum">An index into the list of files.</param>
        /// <param name="section">Section of the INI file this value is in.</param>
        /// <param name="key">Key to lookup.</param>
        /// <returns></returns>
        public string readINIValue(int fileNum, string section, string key)
        {
            return readINIValue(fileNum, section, key, CurrentProfile);
        }

    }
}
